home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 97 / CD-ROM 97 / CD-ROM 97.iso / internet / ghostzilla / ghsetup.exe / chrome / comm.jar / content / navigator / navigatorDD.js < prev    next >
Encoding:
JavaScript  |  2002-05-21  |  26.4 KB  |  712 lines

  1. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Netscape Public License
  6.  * Version 1.1 (the "License"); you may not use this file except in
  7.  * compliance with the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/NPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is 
  18.  * Netscape Communications Corporation.
  19.  * Portions created by the Initial Developer are Copyright (C) 1998
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *  - Kevin Puetz (puetzk@iastate.edu)
  24.  *  - Ben Goodger <ben@netscape.com> 
  25.  *  - Blake Ross <blaker@netscape.com>
  26.  *
  27.  * Alternatively, the contents of this file may be used under the terms of
  28.  * either the GNU General Public License Version 2 or later (the "GPL"), or 
  29.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30.  * in which case the provisions of the GPL or the LGPL are applicable instead
  31.  * of those above. If you wish to allow use of your version of this file only
  32.  * under the terms of either the GPL or the LGPL, and not to allow others to
  33.  * use your version of this file under the terms of the NPL, indicate your
  34.  * decision by deleting the provisions above and replace them with the notice
  35.  * and other provisions required by the GPL or the LGPL. If you do not delete
  36.  * the provisions above, a recipient may use your version of this file under
  37.  * the terms of any one of the NPL, the GPL or the LGPL.
  38.  *
  39.  * ***** END LICENSE BLOCK ***** */
  40.  
  41. var DROP_BEFORE = -1;
  42. var DROP_ON = 0;
  43. var DROP_AFTER = 1;
  44. function _RDF(aType)
  45.   {
  46.     return "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + aType;
  47.   }
  48. function NC_RDF(aType)
  49.   {
  50.     return "http://home.netscape.com/NC-rdf#" + aType;
  51.   }
  52.  
  53. var RDFUtils = {
  54.   getResource: function(aString)
  55.     {
  56.       return this.rdf.GetResource(aString, true);
  57.     },
  58.  
  59.   getTarget: function(aDS, aSourceID, aPropertyID)
  60.     {
  61.       var source = this.getResource(aSourceID);
  62.       var property = this.getResource(aPropertyID);
  63.       return aDS.GetTarget(source, property, true);
  64.     },
  65.  
  66.   getValueFromResource: function(aResource)
  67.     {
  68.       aResource = aResource.QueryInterface(Components.interfaces.nsIRDFResource);
  69.       return aResource ? aResource.Value : null;
  70.     },
  71.   _rdf: null,
  72.   get rdf() {
  73.     if (!this._rdf) {
  74.       this._rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"]
  75.                             .getService(Components.interfaces.nsIRDFService);
  76.     }
  77.     return this._rdf;
  78.   }
  79. };
  80.  
  81. function isBookmark(aURI)
  82.   {
  83.     var db = document.getElementById("innermostBox").database;
  84.     var typeValue = RDFUtils.getTarget(db, aURI, _RDF("type"));
  85.     typeValue = RDFUtils.getValueFromResource(typeValue);
  86.     return (typeValue == NC_RDF("BookmarkSeparator") ||
  87.             typeValue == NC_RDF("Bookmark") ||
  88.             typeValue == NC_RDF("Folder"))
  89.   }
  90.  
  91. var personalToolbarObserver = {
  92.   onDragStart: function (aEvent, aXferData, aDragAction)
  93.     {
  94.       // Prevent dragging out of menus on non Win32 platforms. 
  95.       // a) on Mac drag from menus is generally regarded as being satanic
  96.       // b) on Linux, this causes an X-server crash, see bug 79003. 
  97.       // Since we're not doing D&D into menus properly at this point, it seems
  98.       // fair enough to disable it on non-Win32 platforms. There is no hang
  99.       // or crash associated with this on Windows, so we'll leave the functionality
  100.       // there. 
  101.       if (navigator.platform != "Win32" && aEvent.target.localName != "toolbarbutton")
  102.         return;
  103.  
  104.  
  105.       if (aEvent.target.localName == "menu" || 
  106.          (aEvent.target.localName == "toolbarbutton" && aEvent.target.getAttribute("type") == "menu")) {
  107.         if (aEvent.target.getAttribute("type") == "http://home.netscape.com/NC-rdf#Folder") {
  108.           var child = aEvent.target.childNodes[0];                               
  109.           if (child && child.localName == "menupopup")                                     
  110.             child.hidePopup();                                                  
  111.           else {                                                                 
  112.             var parent = aEvent.target.parentNode;                               
  113.             if (parent && parent.localName == "menupopup")                                 
  114.             parent.hidePopup();                                               
  115.           }                                                                      
  116.         }
  117.       }                                                                         
  118.  
  119.       var personalToolbar = document.getElementById("PersonalToolbar");
  120.       if (aEvent.target == personalToolbar) return;
  121.  
  122.       var db = document.getElementById("innermostBox").database;
  123.       var uri = aEvent.target.id;
  124.       if (!isBookmark(uri)) return;
  125.       var url = RDFUtils.getTarget(db, uri, NC_RDF("URL"));
  126.       if (url)
  127.         url = url.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  128.       else
  129.         url = "";
  130.       var name = RDFUtils.getTarget(db, uri, NC_RDF("Name"));
  131.       if (name)
  132.         name = name.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  133.       else
  134.         name = "";
  135.       var urlString = url + "\n" + name;
  136.       var htmlString = "<A HREF='" + uri + "'>" + name + "</A>";
  137.       aXferData.data = new TransferData();
  138.       aXferData.data.addDataForFlavour("moz/rdfitem", uri);
  139.       aXferData.data.addDataForFlavour("text/x-moz-url", urlString);
  140.       aXferData.data.addDataForFlavour("text/html", htmlString);
  141.       aXferData.data.addDataForFlavour("text/unicode", url);
  142.     },
  143.  
  144.   onDrop: function (aEvent, aXferData, aDragSession)
  145.     {
  146.       var xferData = aXferData.data.split("\n");
  147.       var elementRes = RDFUtils.getResource(xferData[0]);
  148.       var personalToolbarRes = RDFUtils.getResource("NC:PersonalToolbarFolder");
  149.  
  150.       var inner = document.getElementById("innermostBox");
  151.       var childDB = inner.database;
  152.       const kCtrContractID = "@mozilla.org/rdf/container;1";
  153.       const kCtrIID = Components.interfaces.nsIRDFContainer;
  154.       var rdfContainer = Components.classes[kCtrContractID].createInstance(kCtrIID);
  155.  
  156.       // if dragged url is already bookmarked, remove it from current location first
  157.       var parentContainer = findParentContainer(aDragSession.sourceNode);
  158.       if (parentContainer)
  159.         {
  160.           rdfContainer.Init(childDB, parentContainer);
  161.           rdfContainer.RemoveElement(elementRes, false);
  162.         }
  163.  
  164.       // determine charset of link
  165.       var linkCharset = aDragSession.sourceDocument ? aDragSession.sourceDocument.characterSet : null;
  166.       // determine title of link
  167.       var linkTitle;
  168.       
  169.       // look it up in bookmarks
  170.       var bookmarksDS = RDFUtils.rdf.GetDataSource("rdf:bookmarks");
  171.       var nameRes = RDFUtils.getResource(NC_RDF("Name"));
  172.       var nameFromBookmarks = bookmarksDS.GetTarget(elementRes, nameRes, true);
  173.       if (nameFromBookmarks)
  174.         nameFromBookmarks = nameFromBookmarks.QueryInterface(Components.interfaces.nsIRDFLiteral);
  175.       if (nameFromBookmarks) {
  176.         linkTitle = nameFromBookmarks.Value;
  177.       }
  178.       else if (xferData.length >= 2)
  179.         linkTitle = xferData[1]
  180.       else
  181.         {
  182.           // look up this URL's title in global history
  183.           var potentialTitle = null;
  184.           var historyDS = RDFUtils.rdf.GetDataSource("rdf:history");
  185.           var titlePropRes = RDFUtils.getResource(NC_RDF("Name"));
  186.           var titleFromHistory = historyDS.GetTarget(elementRes, titlePropRes, true);
  187.           if (titleFromHistory)
  188.             titleFromHistory = titleFromHistory.QueryInterface(Components.interfaces.nsIRDFLiteral);
  189.           if (titleFromHistory)
  190.             potentialTitle = titleFromHistory.Value;
  191.           linkTitle = potentialTitle;
  192.         }
  193.  
  194.       var dropElement = aEvent.target.id;
  195.       var dropElementRes, dropIndex, dropPosition;
  196.       if (dropElement == "innermostBox") 
  197.         {
  198.           dropElementRes = personalToolbarRes;
  199.           dropPosition = DROP_ON;
  200.         }
  201.       else
  202.         {
  203.           dropElementRes = RDFUtils.getResource(dropElement);
  204.           rdfContainer.Init(childDB, personalToolbarRes);
  205.           dropIndex = rdfContainer.IndexOf(dropElementRes);
  206.           if (dropPosition == undefined)
  207.             dropPosition = determineDropPosition(aEvent, true);
  208.         }
  209.       
  210.       switch (dropPosition) {
  211.       case DROP_BEFORE:
  212.         if (dropIndex<1) dropIndex = 1;
  213.         insertBookmarkAt(xferData[0], linkTitle, linkCharset, personalToolbarRes, dropIndex);
  214.         break;
  215.       case DROP_ON:
  216.         insertBookmarkAt(xferData[0], linkTitle, linkCharset, dropElementRes, -1);
  217.         break;
  218.       case DROP_AFTER:
  219.       default:
  220.         // compensate for badly calculated dropIndex
  221.         rdfContainer.Init(childDB, personalToolbarRes);
  222.         if (dropIndex < rdfContainer.GetCount()) ++dropIndex;
  223.         
  224.         if (dropIndex<0) dropIndex = 0;
  225.         insertBookmarkAt(xferData[0], linkTitle, linkCharset, personalToolbarRes, dropIndex);
  226.         break;
  227.       }
  228.       
  229.       return true;
  230.     },
  231.  
  232.   mCurrentDragOverButton: null,
  233.   mCurrentDragPosition: null,
  234.  
  235.   onDragExit: function (aEvent, aDragSession)
  236.     {
  237.       if (this.mCurrentDragOverButton)
  238.         {
  239.           this.mCurrentDragOverButton.removeAttribute("dragover-left");
  240.           this.mCurrentDragOverButton.removeAttribute("dragover-right");
  241.           this.mCurrentDragOverButton.removeAttribute("dragover-top");
  242.           this.mCurrentDragOverButton.removeAttribute("open");
  243.         }
  244.     },
  245.  
  246.   onDragOver: function (aEvent, aFlavour, aDragSession)
  247.     {
  248.        var dropPosition = determineDropPosition(aEvent, true);
  249.  
  250.       // bail if drop target is not a valid bookmark item or folder
  251.       var inner = document.getElementById("innermostBox");
  252.       if (aEvent.target.parentNode != inner && aEvent.target != inner) 
  253.         {
  254.           aDragSession.canDrop = false;
  255.           return false;
  256.         }
  257.       
  258.       if (this.mCurrentDragOverButton != aEvent.target ||
  259.           (this.mCurrentDragOverButton == aEvent.target &&
  260.            this.mCurrentDragPosition != dropPosition))
  261.         {
  262.           if (this.mCurrentDragOverButton)
  263.             {
  264.               this.mCurrentDragOverButton.removeAttribute("dragover-left");
  265.               this.mCurrentDragOverButton.removeAttribute("dragover-right");
  266.               this.mCurrentDragOverButton.removeAttribute("dragover-top");
  267.               this.mCurrentDragOverButton.removeAttribute("open");
  268.             }
  269.           this.mCurrentDragOverButton = aEvent.target;
  270.           this.mCurrentDragPosition = dropPosition;
  271.         }
  272.  
  273.       switch (dropPosition)
  274.         {
  275.           case DROP_BEFORE: 
  276.             aEvent.target.setAttribute("dragover-left", "true");
  277.             break;
  278.           case DROP_AFTER:
  279.             aEvent.target.setAttribute("dragover-right", "true");
  280.             break;
  281.           case DROP_ON:
  282.           default:
  283.             if (aEvent.target.getAttribute("container") == "true") {
  284.               aEvent.target.setAttribute("dragover-top", "true");
  285.               //cant open a menu during a drag! suck!
  286.               //aEvent.target.setAttribute("open", "true");
  287.             }
  288.             break;
  289.         }
  290.  
  291.        return true;
  292.     },
  293.  
  294.   getSupportedFlavours: function ()
  295.     {
  296.       var flavourSet = new FlavourSet();
  297.       flavourSet.appendFlavour("moz/rdfitem");
  298.       // application/x-moz-file
  299.       flavourSet.appendFlavour("text/unicode");
  300.       return flavourSet;
  301.     }
  302. };
  303.  
  304. var proxyIconDNDObserver = {
  305.   onDragStart: function (aEvent, aXferData, aDragAction)
  306.     {
  307.       var urlBar = document.getElementById("urlbar");
  308.  
  309.       // XXX - do we want to allow the user to set a blank page to their homepage?
  310.       //       if so then we want to modify this a little to set about:blank as
  311.       //       the homepage in the event of an empty urlbar.
  312.       if (!urlBar.value) return;
  313.  
  314.       var urlString = urlBar.value + "\n" + window._content.document.title;
  315.       var htmlString = "<a href=\"" + urlBar.value + "\">" + urlBar.value + "</a>";
  316.  
  317.       aXferData.data = new TransferData();
  318.       aXferData.data.addDataForFlavour("text/x-moz-url", urlString);
  319.       aXferData.data.addDataForFlavour("text/unicode", urlBar.value);
  320.       aXferData.data.addDataForFlavour("text/html", htmlString);
  321.     }
  322. };
  323.  
  324. var homeButtonObserver = {
  325.   onDragStart: function (aEvent, aXferData, aDragAction)
  326.     {
  327.       var homepage = nsPreferences.getLocalizedUnicharPref("browser.startup.homepage", "about:blank");
  328.  
  329.       if (homepage)
  330.         {
  331.           // XXX find a readable title string for homepage, perhaps do a history lookup.
  332.           var htmlString = "<a href=\"" + homepage + "\">" + homepage + "</a>";
  333.           aXferData.data = new TransferData();
  334.           aXferData.data.addDataForFlavour("text/x-moz-url", homepage + "\n" + homepage);
  335.           aXferData.data.addDataForFlavour("text/html", htmlString);
  336.           aXferData.data.addDataForFlavour("text/unicode", homepage);
  337.         }
  338.     },
  339.  
  340.   onDrop: function (aEvent, aXferData, aDragSession)
  341.     {
  342.       var url = retrieveURLFromData(aXferData.data, aXferData.flavour.contentType);
  343.       setTimeout(openHomeDialog, 0, url);
  344.     },
  345.  
  346.   onDragOver: function (aEvent, aFlavour, aDragSession)
  347.     {
  348.       var statusTextFld = document.getElementById("statusbar-display");
  349.       statusTextFld.label = gNavigatorBundle.getString("droponhomebutton");
  350.       aDragSession.dragAction = Components.interfaces.nsIDragService.DRAGDROP_ACTION_LINK;
  351.     },
  352.  
  353.   onDragExit: function (aEvent, aDragSession)
  354.     {
  355.       var statusTextFld = document.getElementById("statusbar-display");
  356.       statusTextFld.label = "";
  357.     },
  358.  
  359.   getSupportedFlavours: function ()
  360.     {
  361.       var flavourSet = new FlavourSet();
  362.       flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
  363.       flavourSet.appendFlavour("text/x-moz-url");
  364.       flavourSet.appendFlavour("text/unicode");
  365.       return flavourSet;
  366.     }
  367. };
  368.  
  369. function openHomeDialog(aURL)
  370. {
  371.   var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
  372.   var promptTitle = gNavigatorBundle.getString("droponhometitle");
  373.   var promptMsg   = gNavigatorBundle.getString("droponhomemsg");
  374.   var okButton    = gNavigatorBundle.getString("droponhomeokbutton");
  375.   var pressedVal = promptService.confirmEx(window, promptTitle, promptMsg,
  376.                           (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0) +
  377.                           (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1),
  378.                           okButton, null, null, null, {value:0});
  379.  
  380.   if (pressedVal == 0) {
  381.     nsPreferences.setUnicharPref("browser.startup.homepage", aURL);
  382.     setTooltipText("home-button", aURL);
  383.   }
  384. }
  385.  
  386. var goButtonObserver = {
  387.   onDragOver: function(aEvent, aFlavour, aDragSession)
  388.     {
  389.       aEvent.target.setAttribute("dragover", "true");
  390.       return true;
  391.     },
  392.   onDragExit: function (aEvent, aDragSession)
  393.     {
  394.       aEvent.target.removeAttribute("dragover");
  395.     },
  396.   onDrop: function (aEvent, aXferData, aDragSession)
  397.     {
  398.       var xferData = aXferData.data.split("\n");
  399.       var uri = xferData[0] ? xferData[0] : xferData[1];
  400.       if (uri)
  401.         loadURI(uri);
  402.     },
  403.   getSupportedFlavours: function ()
  404.     {
  405.       var flavourSet = new FlavourSet();
  406.       flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
  407.       flavourSet.appendFlavour("text/x-moz-url");
  408.       flavourSet.appendFlavour("text/unicode");
  409.       return flavourSet;
  410.     }
  411. };
  412.  
  413. var searchButtonObserver = {
  414.   onDragOver: function(aEvent, aFlavour, aDragSession)
  415.     {
  416.       aEvent.target.setAttribute("dragover", "true");
  417.       return true;
  418.     },
  419.   onDragExit: function (aEvent, aDragSession)
  420.     {
  421.       aEvent.target.removeAttribute("dragover");
  422.     },
  423.   onDrop: function (aEvent, aXferData, aDragSession)
  424.     {
  425.       var xferData = aXferData.data.split("\n");
  426.       var uri = xferData[1] ? xferData[1] : xferData[0];
  427.       if (uri)
  428.         OpenSearch('internet',false, uri);
  429.     },
  430.   getSupportedFlavours: function ()
  431.     {
  432.       var flavourSet = new FlavourSet();
  433.       flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
  434.       flavourSet.appendFlavour("text/x-moz-url");
  435.       flavourSet.appendFlavour("text/unicode");
  436.       return flavourSet;
  437.     }
  438. };
  439. var gOpenFolder = null;
  440.  
  441. function closeFolder (aTarget)
  442. {
  443.   var children = aTarget.childNodes;
  444.   for (var i=0; i<children.length; i++) {
  445.     if (children[i].getAttribute("open") == "true") {
  446.       closeFolder(children[i].firstChild);
  447.       children[i].firstChild.hidePopup();
  448.     }
  449.   }
  450. }
  451.  
  452. var folderObserver = {
  453.   onDragOver: function(aEvent, aFlavour, aDragSession)
  454.     {
  455.       // XXX disable dropping onto toolbar folders on unices
  456.       // XXX see <http://bugzilla.mozilla.org/show_bug.cgi?id=96504#c131>
  457.       if (navigator.appVersion.indexOf("X11") != -1)
  458.         return false;
  459.  
  460.       if (aEvent.target.getAttribute("open") == "true")
  461.         return false;
  462.  
  463.       aEvent.target.setAttribute("dragover", "true");
  464.  
  465.       // XXX Ensure that only one folder can be open at once
  466.       document.getElementById("bookmarks-button").firstChild.hidePopup();
  467.       closeFolder(document.getElementById("bookmarks-button").firstChild);
  468.       closeFolder(document.getElementById("innermostBox"));
  469.  
  470.       aEvent.target.firstChild.showPopup(aEvent.target, -1, -1, "menupopup", "bottomleft", "bottomleft");
  471.  
  472.       return true;
  473.     },
  474.   getSupportedFlavours: function ()
  475.     {
  476.       var flavourSet = new FlavourSet();
  477.       flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
  478.       flavourSet.appendFlavour("text/x-moz-url");
  479.       return flavourSet;
  480.     }
  481. };
  482.  
  483. var gCurrentTarget = null;
  484. var gCurrentDragOverMenu = null;
  485. function closeOpenMenu()
  486. {
  487.   if (gCurrentDragOverMenu && gCurrentTarget.firstChild != gCurrentDragOverMenu) {
  488.     if (gCurrentTarget.parentNode != gCurrentDragOverMenu) {
  489.       gCurrentDragOverMenu.hidePopup();
  490.       gCurrentDragOverMenu = null;
  491.     }
  492.   }
  493. }
  494.         
  495. var menuDNDObserver = {
  496.   onDragOver: function(aEvent, aFlavour, aDragSession) 
  497.   {
  498.     // if we're a folder just one level deep, open it
  499.     var isOneLevelDeep = aEvent.target.parentNode.parentNode.getAttribute("open") == "true" && aEvent.target.parentNode.parentNode.localName == "toolbarbutton";
  500.     var dropPosition = determineDropPosition(aEvent, !isOneLevelDeep);
  501.     gCurrentTarget = aEvent.target;
  502.     if (aEvent.target.firstChild && aEvent.target.firstChild.localName == "menupopup") {
  503.       if (isOneLevelDeep) {
  504.         if (gCurrentDragOverMenu && gCurrentDragOverMenu != aEvent.target.firstChild)
  505.           gCurrentDragOverMenu.hidePopup();
  506.         if (!gCurrentDragOverMenu) {
  507.           aEvent.target.firstChild.showPopup(aEvent.target, -1, -1, "menupopup", "topright, topright");
  508.           gCurrentDragOverMenu = aEvent.target.firstChild;
  509.         }
  510.       }
  511.       else {
  512.         aEvent.target.setAttribute("menuactive", "true"); 
  513.       }
  514.     }
  515.   
  516.     // remove drag attributes from old item once we move to a new item
  517.     if (this.mCurrentDragOverItem != aEvent.target) {
  518.       if (this.mCurrentDragOverItem) {
  519.         this.mCurrentDragOverItem.removeAttribute("dragover-top");
  520.         this.mCurrentDragOverItem.removeAttribute("dragover-bottom");
  521.         this.mCurrentDragOverItem.removeAttribute("menuactive");
  522.       }
  523.       this.mCurrentDragOverItem = aEvent.target;
  524.     }
  525.     
  526.     // if there's an open submenu and we're not over it or one of its children, close it
  527.     if (gCurrentDragOverMenu && aEvent.target.firstChild != gCurrentDragOverMenu) {
  528.       if (aEvent.target.parentNode != gCurrentDragOverMenu) {
  529.         setTimeout(function() { closeOpenMenu(); },500);
  530.       }
  531.     }
  532.  
  533.     // ensure appropriate feedback
  534.     switch (dropPosition) {
  535.       case DROP_BEFORE: 
  536.         aEvent.target.setAttribute("dragover-bottom", "true");
  537.         break;
  538.       case DROP_AFTER:
  539.         aEvent.target.setAttribute("dragover-top", "true");
  540.         break;
  541.     }
  542.   },
  543.   mCurrentDragOverItem: null,
  544.   onDragExit: function (aEvent, aDragSession)
  545.   {
  546.     // remove drag attribute from current item once we leave the popup
  547.     if (this.mCurrentDragOverItem) {
  548.       this.mCurrentDragOverItem.removeAttribute("dragover-top");
  549.       this.mCurrentDragOverItem.removeAttribute("dragover-bottom");
  550.     }
  551.   },
  552.   onDrop: function (aEvent, aXferData, aDragSession)
  553.   {
  554.     var xferData = aXferData.data.split("\n");
  555.     var elementRes = RDFUtils.getResource(xferData[0]);
  556.  
  557.     var bookmarksButton = document.getElementById("bookmarks-button");
  558.     var childDB = bookmarksButton.database;
  559.     var rdfContainer = Components.classes["@mozilla.org/rdf/container;1"].createInstance(Components.interfaces.nsIRDFContainer);
  560.  
  561.     // if dragged url is already bookmarked, remove it from current location first
  562.     var parentContainer = findParentContainer(aDragSession.sourceNode);
  563.     if (parentContainer) {
  564.       rdfContainer.Init(childDB, parentContainer);
  565.       rdfContainer.RemoveElement(elementRes, false);
  566.     }
  567.     parentContainer = findParentContainer(aEvent.target);
  568.     // determine charset of link
  569.     var linkCharset = aDragSession.sourceDocument ? aDragSession.sourceDocument.characterSet : null;
  570.     // determine title of link
  571.     var linkTitle;
  572.     // look it up in bookmarks
  573.     var bookmarksDS = RDFUtils.rdf.GetDataSource("rdf:bookmarks");
  574.     var nameRes = RDFUtils.getResource(NC_RDF("Name"));
  575.     var nameFromBookmarks = bookmarksDS.GetTarget(elementRes, nameRes, true);
  576.     if (nameFromBookmarks)
  577.       nameFromBookmarks = nameFromBookmarks.QueryInterface(Components.interfaces.nsIRDFLiteral);
  578.  
  579.     if (nameFromBookmarks)
  580.       linkTitle = nameFromBookmarks.Value;
  581.     else if (xferData.length >= 2)
  582.       linkTitle = xferData[1]
  583.     else {
  584.       // look up this URL's title in global history
  585.       var historyDS = RDFUtils.rdf.GetDataSource("rdf:history");
  586.       var titlePropRes = RDFUtils.getResource(NC_RDF("Name"));
  587.       var titleFromHistory = historyDS.GetTarget(elementRes, titlePropRes, true);
  588.       if (titleFromHistory)
  589.         titleFromHistory = titleFromHistory.QueryInterface(Components.interfaces.nsIRDFLiteral);
  590.       if (titleFromHistory)
  591.         linkTitle = titleFromHistory.Value;
  592.     }
  593.  
  594.     var dropElement = aEvent.target.id;
  595.     var dropElementRes, dropIndex, dropPosition;
  596.     dropElementRes = RDFUtils.getResource(dropElement);
  597.     rdfContainer.Init(childDB, parentContainer);
  598.     dropIndex = rdfContainer.IndexOf(dropElementRes);
  599.     var isOneLevelDeep = aEvent.target.parentNode.parentNode.getAttribute("open") == "true" && aEvent.target.parentNode.parentNode.localName == "toolbarbutton";
  600.     dropPosition = determineDropPosition(aEvent, !isOneLevelDeep);
  601.     switch (dropPosition) {
  602.       case DROP_BEFORE:
  603.         --dropIndex;
  604.         if (dropIndex<1) dropIndex = 1;
  605.           insertBookmarkAt(xferData[0], linkTitle, linkCharset, parentContainer, dropIndex);
  606.         break;
  607.       case DROP_ON:
  608.         insertBookmarkAt(xferData[0], linkTitle, linkCharset, dropElementRes, -1);
  609.         break;
  610.       case DROP_AFTER:
  611.       default:
  612.         // compensate for badly calculated dropIndex
  613.         if (dropIndex < rdfContainer.GetCount()) ++dropIndex;
  614.          
  615.         if (dropIndex<0) dropIndex = 0;
  616.         --dropIndex;
  617.         insertBookmarkAt(xferData[0], linkTitle, linkCharset, parentContainer, dropIndex);
  618.         break;
  619.     }
  620.     
  621.     // if user isn't rearranging within the menu, close it
  622.     if (aDragSession.sourceNode.localName != "menuitem" && aDragSession.sourceNode.localName != "menu")
  623.       setTimeout(function() { if (gCurrentDragOverMenu) gCurrentDragOverMenu.hidePopup(); document.getElementById("bookmarks-button").firstChild.hidePopup(); gDidOpen = false; }, 190);    
  624.     
  625.     return true;
  626.   },
  627.  
  628.   getSupportedFlavours: function () {
  629.     var flavourSet = new FlavourSet();
  630.     flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
  631.     flavourSet.appendFlavour("text/x-moz-url");
  632.     return flavourSet;
  633.   }
  634. };
  635.  
  636. function determineDropPosition(aEvent, aAllowDropOn)
  637. {
  638.   var overButtonBoxObject = aEvent.target.boxObject.QueryInterface(Components.interfaces.nsIBoxObject);
  639.   // most things only drop on the left or right
  640.   var regionCount = 2;
  641.  
  642.   // you can drop ONTO containers, so there is a "middle" region
  643.   if (aAllowDropOn && aEvent.target.getAttribute("container") == "true" &&
  644.       aEvent.target.getAttribute("type") == "menu")
  645.     return DROP_ON;
  646.       
  647.   var measure;
  648.   var coordValue;
  649.   var clientCoordValue;
  650.   if (aEvent.target.localName == "menuitem" || aEvent.target.localName == "menu") {
  651.     measure = overButtonBoxObject.height/regionCount;
  652.     coordValue = overButtonBoxObject.y;
  653.     clientCoordValue = aEvent.clientY;
  654.   }
  655.   else if (aEvent.target.localName == "toolbarbutton") {
  656.     measure = overButtonBoxObject.width/regionCount;
  657.     coordValue = overButtonBoxObject.x;
  658.     clientCoordValue = aEvent.clientX;
  659.   }
  660.   else
  661.     return 0;
  662.  
  663.       
  664.   // in the first region?
  665.   if (clientCoordValue < (coordValue + measure))
  666.     return DROP_BEFORE;
  667.  
  668.   // in the last region?
  669.   if (clientCoordValue >= (coordValue + (regionCount - 1)*measure))
  670.     return DROP_AFTER;
  671.  
  672.   // must be in the middle somewhere
  673.   return DROP_ON;
  674. }
  675.  
  676. // returns the parent resource of the dragged element. This is determined
  677. // by inspecting the source element of the drag and walking up the DOM tree
  678. // to find the appropriate containing node.
  679. function findParentContainer(aElement)
  680. {
  681.   if (!aElement) return null;
  682.   switch (aElement.localName) {
  683.     case "toolbarbutton":
  684.       var box = aElement.parentNode;
  685.       return RDFUtils.getResource(box.getAttribute("ref"));
  686.     case "menu":
  687.     case "menuitem":
  688.       var parentNode = aElement.parentNode.parentNode;
  689.      
  690.       if (parentNode.getAttribute("type") != NC_RDF("Folder") &&
  691.           parentNode.getAttributeNS("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "type") != "http://home.netscape.com/NC-rdf#Folder")
  692.         return RDFUtils.getResource("NC:BookmarksRoot");
  693.       return RDFUtils.getResource(parentNode.id);
  694.     case "treecell":
  695.       var treeitem = aElement.parentNode.parentNode.parentNode.parentNode;
  696.       var res = treeitem.getAttribute("ref");
  697.       if (!res)
  698.         res = treeitem.id;            
  699.       return RDFUtils.getResource(res);
  700.   }
  701.   return null;
  702. }
  703.  
  704. function insertBookmarkAt(aURL, aTitle, aCharset, aFolderRes, aIndex)
  705. {
  706.   const kBMSContractID = "@mozilla.org/browser/bookmarks-service;1";
  707.   const kBMSIID = Components.interfaces.nsIBookmarksService;
  708.   const kBMS = Components.classes[kBMSContractID].getService(kBMSIID);
  709.   kBMS.createBookmarkWithDetails(aTitle, aURL, aCharset, aFolderRes, aIndex);
  710. }
  711.  
  712.